﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.IO;
using Lot.WebClient.Response;
using System.IO.Compression;
using System.Collections.Concurrent;
using System.Threading;


namespace Lot.WebClient
{
    internal static class HttpRequestEngine
    {
        private static readonly int MaxConcurrentConnectionsToDomain = 2;

        static HttpRequestEngine()
        {
            //Speedup https requests
            ServicePointManager.ServerCertificateValidationCallback = new System.Net.Security.RemoteCertificateValidationCallback((a, b, c, d) => { return true; });

            ServicePointManager.DefaultConnectionLimit = 250;
        }


        public static String GetResponseString(HttpWebResponse httpWebResponse, Encoding encodingOverride = null)
        {
            String returnString;
            Encoding encoding=null;
            using (var stream = GetResponseStream(httpWebResponse, out encoding))
            {
                returnString = GetResponseString(stream, encodingOverride ?? encoding);
            }

            return returnString;
        }

        public static String GetResponseString(Stream stream, Encoding encoding = null)
        {
            String returnString = null;
                        
            StreamReader reader;
            stream.Position = 0;
            if (encoding != null)
                reader = new StreamReader(stream, encoding);
            else
                reader = new StreamReader(stream);

            returnString= reader.ReadToEnd();

            return returnString;
           

        }

        public static Stream GetResponseStream(HttpWebResponse httpWebResponse, out Encoding encoding)
        {

            bool compressed = false;
            encoding = null;

            MemoryStream ms = new MemoryStream();

            Stream stream = null;

            using (stream = httpWebResponse.GetResponseStream())
            {
                if (httpWebResponse.ContentEncoding.ToLower().Contains("gzip"))
                {
                    stream = new GZipStream(stream, CompressionMode.Decompress);
                    compressed = true;
                }
                else if (httpWebResponse.ContentEncoding.ToLower().Contains("deflate"))
                {
                    stream = new DeflateStream(stream, CompressionMode.Decompress);
                    compressed = true;
                }


                stream.CopyTo(ms);
            }

            if (!compressed)
            {
                var contentEncoding = httpWebResponse.ContentEncoding;
                if (!string.IsNullOrEmpty(contentEncoding))
                    encoding = Encoding.GetEncoding(contentEncoding);

            }

            if (encoding == null)
            {

                if (!string.IsNullOrEmpty(httpWebResponse.CharacterSet))
                {
                    var charset = httpWebResponse.CharacterSet.ToLower();
                    if (charset.StartsWith("cp"))
                    {
                        var cp = Int32.Parse(charset.Replace("cp-",""));
                        encoding = Encoding.GetEncoding(cp);
                    }
                    else
                    {
                        encoding = Encoding.GetEncoding(httpWebResponse.CharacterSet);
                    }
                }
                else
                {
                    encoding = null;
                }

            }


            ms.Position = 0;

            return ms;

        }

        private static ConcurrentDictionary<string, int> currentRequests = new ConcurrentDictionary<string, int>();

        public static WebClientResponse GetHttpResponseSafe(string url, CookieContainer cookieContainer, RequestConfiguration config)
        {
            int current = 0;
            var host = DomainExtractor.DomainExtractor.GetDomain(url);

            while (currentRequests.GetOrAdd(host, 0) >= MaxConcurrentConnectionsToDomain)
            {
                Thread.Sleep(20 * currentRequests.GetOrAdd(host, 0));
            }

            current = currentRequests.AddOrUpdate(host, 1, (x, y) => y + 1);

            var response = GetHttpResponse(url, cookieContainer, config);

            current = currentRequests.AddOrUpdate(host, 0, (x, y) => y-1);
            if (current == 0)            
                currentRequests.TryRemove(host, out current);

            return response;
                
            
        }

        public static WebClientResponse GetHttpResponse(string url, CookieContainer cookieContainer, RequestConfiguration config)
        {
                        
            try
            {
                HttpWebResponse httpWebResponse = null;

                var httpWebRequest = WebRequest.Create(url) as HttpWebRequest;
                httpWebRequest.Method = config.HttpMethod.ToString();
                httpWebRequest.AllowAutoRedirect = config.AutoFollowRedirects;
                httpWebRequest.UserAgent = config.UserAgent.Name;
                httpWebRequest.Timeout = config.Timeout;
                httpWebRequest.CookieContainer = cookieContainer;
                httpWebRequest.KeepAlive = config.TryKeepAlive;

                if (config.Accept != null)
                    httpWebRequest.Accept = config.Accept.MimeString;

                if (config.EnableCompression)
                    httpWebRequest.Headers.Add(HttpRequestHeader.AcceptEncoding, "gzip,deflate");

                if (!string.IsNullOrEmpty(config.AcceptCharset))
                    httpWebRequest.Headers.Add(HttpRequestHeader.AcceptCharset, config.AcceptCharset);

                if (!string.IsNullOrEmpty(config.Referer))
                    httpWebRequest.Referer = config.Referer;

                if (!string.IsNullOrEmpty(config.Origin))
                    httpWebRequest.Headers.Add("Origin", config.Origin);

                if (!string.IsNullOrEmpty(config.Host))
                    httpWebRequest.Headers.Add(HttpRequestHeader.Host, config.Host);

                httpWebRequest.Proxy = config.Proxy;


                //If POST 
                if (config.HttpMethod == HttpMethods.POST)
                {

                    byte[] byteArray = Encoding.UTF8.GetBytes(config.PostData.GetDataString());
                    // Set the ContentType property of the WebRequest.
                    httpWebRequest.ContentType = config.PostData.ContentType.MimeString;
                    // Set the ContentLength property of the WebRequest.
                    httpWebRequest.ContentLength = byteArray.Length;
                    // Get the request stream.
                    Stream dataStream = httpWebRequest.GetRequestStream();
                    // Write the data to the request stream.
                    dataStream.Write(byteArray, 0, byteArray.Length);
                    // Close the Stream object.
                    dataStream.Close();

                }

                httpWebResponse = httpWebRequest.GetResponse() as HttpWebResponse;

                return new WebClientResponse() { HttpResponse = httpWebResponse, Success = true };
            }
            catch (System.Net.WebException ex)
            {
                var response = new WebClientResponse();

                response.Exception = ex;
                response.HttpResponse = ex.Response as HttpWebResponse;

                response.Success = response.HttpResponse != null;

                return response;
            }

        }

    }
}
